﻿/** 
 * Copyright 2010 NUMKIT.COM (contact: opensource@numkit.com)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 * @author 陈超
 * @version 1.0
 */
class DrawPanel {
	private var formatter:TextFormat;
	private var config:Config;
	private var data:Data;
	private var aPiece:Array;
	private var maxTextSpace:Number;
	private var textHeight:Number;
	private var aLabel:Array;
	private var aLabelField:Array;
	private var aSectorClip:Array;
	private var r:Number;
	private var centerX:Number;
	private var centerY:Number;
	private var border:Number = 3;
	private var outerSpace:Number = 5;
	private var outestSpace:Number = 5;
	private var aAngle:Array;
	private var aInPiecePos:Array;
	private var aOutPiecePos:Array;
	private var aOuterPiecePos:Array;
	private var aOutestPiecePos:Array;
	private var mc:MovieClip;
	public function DrawPanel(config:Config, data:Data) {
		this.config = config;
		this.data = data;
		//check
		if (data.aPiece.length == 0) {
			return;
		}
		if (data.valueTotal == 0) {
			return;
		}
		//aPiece maxTextSpace textHeight aLabel                           
		aPiece = data.aPiece;
		formatter = Config.formatter;
		var maxSpace:Number = 0;
		var maxHeight:Number = 0;
		aLabel = new Array();
		for (var i:Number = 0; i<aPiece.length; i++) {
			var label:String = aPiece[i].getLabel(config.labelFormat);
			aLabel[i] = label;
			var fontSize:Object = formatter.getTextExtent(label);
			if (fontSize.textFieldHeight>maxHeight) {
				maxHeight = fontSize.textFieldHeight;
			}
			//trace(label+":"+fontSize.textFieldWidth+":"+textHeight);                        
			if (fontSize.textFieldWidth>maxSpace) {
				maxSpace = fontSize.textFieldWidth;
			}
		}
		textHeight = maxHeight;
		maxTextSpace = maxSpace;
		//r
		var r1:Number = Stage.width/2-border-maxTextSpace-2-10-outestSpace-outerSpace;
		var r2:Number = Stage.height/2-border-textHeight/2-10;
		r = r1<r2 ? r1 : r2;
		//centerX centerY
		centerX = Stage.width/2;
		centerY = Stage.height/2;
		//aAngle
		aAngle = new Array();
		var valueTotal = data.valueTotal;
		for (var i:Number = 0; i<aPiece.length; i++) {
			aAngle[i] = aPiece[i].getValue()*2*Math.PI/valueTotal;
		}
		//aInPiecePos aOutPiecePos aOuterPiecePos aOutestPiecePos
		aInPiecePos = new Array();
		aOutPiecePos = new Array();
		aOuterPiecePos = new Array();
		aOutestPiecePos = new Array();
		for (var i:Number = 0; i<aPiece.length; i++) {
			var aMid:Number = Global.rotate;
			for (var j:Number = 0; j<i; j++) {
				aMid += aAngle[j];
			}
			aMid += aAngle[i]/2;
			var inX:Number = centerX+r*0.618*Math.cos(aMid);
			var inY:Number = centerY-r*0.618*Math.sin(aMid);
			var outX:Number = centerX+(r+10)*Math.cos(aMid);
			var outY:Number = centerY-(r+10)*Math.sin(aMid);
			var outerX:Number = outX<centerX ? centerX-r-10-outerSpace : centerX+r+10+outerSpace;
			var outerY:Number = outY;
			aInPiecePos[i] = new Point(inX, inY);
			aOutPiecePos[i] = new Point(outX, outY);
			aOuterPiecePos[i] = new Point(outerX, outerY);
		}
		for (var i:Number = 0; i<aPiece.length; i++) {
			var outerX:Number = aOuterPiecePos[i].x;
			var outerY:Number = aOuterPiecePos[i].y;
			var outestX:Number = outerX<centerX ? outerX-outestSpace : outerX+outestSpace;
			var outestY:Number = outerY;
			aOutestPiecePos[i] = new Point(outestX, outestY);
		}
		adjustOutestPiecePos();
	}
	private function adjustOutestPiecePos():Void {
		var aLeftY:Array = new Array();
		var aRightY:Array = new Array();
		for (var i:Number = 0; i<aOutestPiecePos.length; i++) {
			if (aOutestPiecePos[i].x<centerX) {
				aLeftY.push({index:i, value:aOutestPiecePos[i].y});
			} else {
				aRightY.push({index:i, value:aOutestPiecePos[i].y});
			}
		}
		adjustSideOutest(aLeftY);
		adjustSideOutest(aRightY);
	}
	private function adjustSideOutest(aSideY:Array):Void {
		aSideY.sortOn("value", Array.NUMERIC);
		var aGroup:Array = new Array();
		for (var i:Number = 0; i<aSideY.length; i++) {
			var groupMember:Array = new Array();
			groupMember.push(aSideY[i]);
			aGroup.push({top:aSideY[i].value-textHeight/2, bottom:aSideY[i].value+textHeight/2, member:groupMember});
		}
		mergeGroupOutest(aGroup);
	}
	private function mergeGroupOutest(aGroup:Array):Void {
		if (aGroup.length == 0) {
			return;
		}
		//check conflict  
		var conflict:Boolean = false;
		for (var i:Number = 0; i<aGroup.length; i++) {
			if (i>=1) {
				if (aGroup[i].top<aGroup[i-1].bottom) {
					conflict = true;
				}
			}
		}
		if (!conflict) {
			updateOutest(aGroup);
			return;
		}
		//check merge finish  
		if (aGroup.length == 1) {
			updateOutest(aGroup);
			return;
		}
		var group:Object;
		var merging:Boolean = false;
		for (var i:Number = 0; i<aGroup.length; i++) {
			if (i>=1) {
				if (aGroup[i].top<aGroup[i-1].bottom) {
					if (!merging) {
						group = aGroup[i-1];
					}
					group.member = group.member.concat(aGroup[i].member);
					aGroup[i].member = undefined;
					merging = true;
				} else {
					merging = false;
				}
			}
		}
		var aNewGroup:Array = new Array();
		for (var i:Number = 0; i<aGroup.length; i++) {
			if (aGroup[i].member != undefined) {
				adjustGroupOutest(aGroup[i]);
				aNewGroup.push(aGroup[i]);
			}
		}
		mergeGroupOutest(aNewGroup);
	}
	private function adjustGroupOutest(group:Object):Void {
		var member:Array = group.member;
		if (member.length != 1) {
			var cp:Number = (member[0].value+member[member.length-1].value)/2;
			if (cp>Stage.height-border-member.length*textHeight/2) {
				cp = Stage.height-border-member.length*textHeight/2;
			}
			if (cp<border+member.length*textHeight/2) {
				cp = border+member.length*textHeight/2;
			}
			for (var i:Number = 0; i<member.length; i++) {
				member[i].value = cp+(2*i+1-member.length)*textHeight/2;
			}
		}
		group.top = member[0].value-textHeight/2;
		group.bottom = member[member.length-1].value+textHeight/2;
	}
	private function updateOutest(aGroup:Array):Void {
		//trace("update:");
		for (var i:Number = 0; i<aGroup.length; i++) {
			var member:Array = aGroup[i].member;
			//trace("group:");
			for (var j:Number = 0; j<member.length; j++) {
				aOutestPiecePos[member[j].index].y = member[j].value;
				//if (aOutestPiecePos[member[j].index].x>=centerX) {
				//trace(member[j].index+":"+j+":"+member[j].value+" right");
				//}else{
				//trace(member[j].index+":"+j+":"+member[j].value+" left");
				//}
			}
		}
	}
	public function draw(mc:MovieClip):Void {
		this.mc = mc;
		//background   
		mc.beginFill(config.backgroundColor, 100);
		mc.moveTo(0, 0);
		mc.lineTo(0, Stage.height);
		mc.lineTo(Stage.width, Stage.height);
		mc.lineTo(Stage.width, 0);
		mc.lineTo(0, 0);
		mc.endFill();
		//check
		if (data.aPiece.length == 0) {
			return;
		}
		if (data.valueTotal == 0) {
			return;
		}
		//sector        
		aSectorClip = new Array();
		for (var i:Number = 0; i<aPiece.length; i++) {
			var aStart:Number = 0;
			for (var j:Number = 0; j<i; j++) {
				aStart += aAngle[j];
			}
			var aEnd:Number = aStart+aAngle[i];
			aSectorClip[i] = mc.createEmptyMovieClip("piece"+i, mc.getNextHighestDepth());
			fillSector(aSectorClip[i], aPiece[i].getColor(), centerX, centerY, r, Global.rotate+aStart, Global.rotate+aEnd);
			aPiece[i].registerClickEvent(aSectorClip[i]);
		}
		//line
		for (var i:Number = 0; i<aPiece.length; i++) {
			var inX:Number = aInPiecePos[i].x;
			var inY:Number = aInPiecePos[i].y;
			var outX:Number = aOutPiecePos[i].x;
			var outY:Number = aOutPiecePos[i].y;
			var outerX:Number = aOuterPiecePos[i].x;
			var outerY:Number = aOuterPiecePos[i].y;
			var outestX:Number = aOutestPiecePos[i].x;
			var outestY:Number = aOutestPiecePos[i].y;
			mc.lineStyle(1, config.directLineColor, 100);
			mc.moveTo(inX, inY);
			mc.lineTo(outX, outY);
			mc.lineTo(outerX, outerY);
			mc.lineTo(outestX, outestY);
		}
		//label
		aLabelField = new Array();
		for (var i:Number = 0; i<aPiece.length; i++) {
			var fontSize:Object = formatter.getTextExtent(aLabel[i]);
			var outestX:Number = aOutestPiecePos[i].x;
			var outestY:Number = aOutestPiecePos[i].y;
			var xPos:Number = outestX<centerX ? outestX-fontSize.textFieldWidth-2 : outestX+2;
			var yPos:Number = outestY-fontSize.textFieldHeight/2;
			aLabelField[i] = mc.createTextField("tField"+i, mc.getNextHighestDepth(), Stage.width, Stage.height, fontSize.textFieldWidth, fontSize.textFieldHeight);
			aLabelField[i].selectable = false;
			aLabelField[i].text = aLabel[i];
			aLabelField[i].setTextFormat(formatter);
			aLabelField[i]._x = xPos;
			aLabelField[i]._y = yPos;
		}
		Mouse.addListener(this);
	}
	public function onMouseWheel(delta:Number):Void {
		if (delta>0) {
			Global.rotate += Math.PI/12;
			if (Global.rotate>2*Math.PI) {
				Global.rotate -= 2*Math.PI;
			}
			redraw();
		} else if (delta<0) {
			Global.rotate -= Math.PI/12;
			if (2*Math.PI<0) {
				Global.rotate += 2*Math.PI;
			}
			redraw();
		}
	}
	private function fillSector(mc:MovieClip, color:Number, cx:Number, cy:Number, r:Number, aFrom:Number, aTo:Number):Void {
		mc.lineStyle(0, color, 100);
		mc.beginFill(color, 100);
		var x:Number = cx+r*Math.cos(aFrom);
		var y:Number = cy-r*Math.sin(aFrom);
		mc.moveTo(cx, cy);
		mc.lineTo(x, y);
		var step = 1/r;
		var aRange = Math.abs(aTo-aFrom);
		if (aRange != 0) {
			step = aRange<step ? aRange : step;
		}
		//trace("step="+step+"from="+(aFrom+step)+":"+"to="+aTo+"count="+((aTo-aFrom-step)/step));                        
		for (var a:Number = aFrom+step; a<=aTo; a += step) {
			x = cx+r*Math.cos(a);
			y = cy-r*Math.sin(a);
			mc.lineTo(x, y);
		}
		mc.lineTo(cx, cy);
		mc.endFill();
	}
	public function redraw():Void {
		//check
		if (data.aPiece.length == 0) {
			return;
		}
		if (data.valueTotal == 0) {
			return;
		}
		//aInPiecePos aOutPiecePos aOuterPiecePos aOutestPiecePos              
		aInPiecePos = new Array();
		aOutPiecePos = new Array();
		aOuterPiecePos = new Array();
		aOutestPiecePos = new Array();
		for (var i:Number = 0; i<aPiece.length; i++) {
			var aMid:Number = Global.rotate;
			for (var j:Number = 0; j<i; j++) {
				aMid += aAngle[j];
			}
			aMid += aAngle[i]/2;
			var inX:Number = centerX+r*0.618*Math.cos(aMid);
			var inY:Number = centerY-r*0.618*Math.sin(aMid);
			var outX:Number = centerX+(r+10)*Math.cos(aMid);
			var outY:Number = centerY-(r+10)*Math.sin(aMid);
			var outerX:Number = outX<centerX ? centerX-r-10-outerSpace : centerX+r+10+outerSpace;
			var outerY:Number = outY;
			aInPiecePos[i] = new Point(inX, inY);
			aOutPiecePos[i] = new Point(outX, outY);
			aOuterPiecePos[i] = new Point(outerX, outerY);
		}
		for (var i:Number = 0; i<aPiece.length; i++) {
			var outerX:Number = aOuterPiecePos[i].x;
			var outerY:Number = aOuterPiecePos[i].y;
			var outestX:Number = outerX<centerX ? outerX-outestSpace : outerX+outestSpace;
			var outestY:Number = outerY;
			aOutestPiecePos[i] = new Point(outestX, outestY);
		}
		adjustOutestPiecePos();
		//clear
		mc.clear();
		//background   
		mc.beginFill(config.backgroundColor, 100);
		mc.moveTo(0, 0);
		mc.lineTo(0, Stage.height);
		mc.lineTo(Stage.width, Stage.height);
		mc.lineTo(Stage.width, 0);
		mc.lineTo(0, 0);
		mc.endFill();
		//check
		if (data.aPiece.length == 0) {
			return;
		}
		if (data.valueTotal == 0) {
			return;
		}
		//sector                            
		for (var i:Number = 0; i<aPiece.length; i++) {
			var aStart:Number = 0;
			for (var j:Number = 0; j<i; j++) {
				aStart += aAngle[j];
			}
			var aEnd:Number = aStart+aAngle[i];
			aSectorClip[i].clear();
			fillSector(aSectorClip[i], aPiece[i].getColor(), centerX, centerY, r, Global.rotate+aStart, Global.rotate+aEnd);
			//aPiece[i].registerClickEvent(pieceMC);
		}
		//line
		for (var i:Number = 0; i<aPiece.length; i++) {
			var inX:Number = aInPiecePos[i].x;
			var inY:Number = aInPiecePos[i].y;
			var outX:Number = aOutPiecePos[i].x;
			var outY:Number = aOutPiecePos[i].y;
			var outerX:Number = aOuterPiecePos[i].x;
			var outerY:Number = aOuterPiecePos[i].y;
			var outestX:Number = aOutestPiecePos[i].x;
			var outestY:Number = aOutestPiecePos[i].y;
			mc.lineStyle(1, config.directLineColor, 100);
			mc.moveTo(inX, inY);
			mc.lineTo(outX, outY);
			mc.lineTo(outerX, outerY);
			mc.lineTo(outestX, outestY);
		}
		//label
		for (var i:Number = 0; i<aPiece.length; i++) {
			var fontSize:Object = formatter.getTextExtent(aLabel[i]);
			var outestX:Number = aOutestPiecePos[i].x;
			var outestY:Number = aOutestPiecePos[i].y;
			var xPos:Number = outestX<centerX ? outestX-fontSize.textFieldWidth-2 : outestX+2;
			var yPos:Number = outestY-fontSize.textFieldHeight/2;
			aLabelField[i]._x = xPos;
			aLabelField[i]._y = yPos;
		}
	}
}
